/******************************************************************************* * Copyright (c) 2003, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ui.internal.presentations; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; //import org.eclipse.swt.events.MouseAdapter; //import org.eclipse.swt.events.MouseEvent; //import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.events.TraverseEvent; import org.eclipse.swt.events.TraverseListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Layout; //import org.eclipse.swt.widgets.Menu; //import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableItem; import org.eclipse.swt.widgets.Text; //import org.eclipse.ui.internal.WorkbenchMessages; import org.eclipse.ui.internal.misc.StringMatcher; import org.eclipse.ui.internal.presentations.defaultpresentation.DefaultTabItem; /** */ public abstract class AbstractTableInformationControl { /** * The NamePatternFilter selects the elements which match the given string * patterns. */ protected class NamePatternFilter extends ViewerFilter { public NamePatternFilter() { //no-op } /* * (non-Javadoc) Method declared on ViewerFilter. */ public boolean select(Viewer viewer, Object parentElement, Object element) { StringMatcher matcher = getMatcher(); if (matcher == null || !(viewer instanceof TableViewer)) { return true; } TableViewer tableViewer = (TableViewer) viewer; String matchName = ((ILabelProvider) tableViewer.getLabelProvider()) .getText(element); if(matchName == null) { return false; } // A dirty editor's label will start with dirty prefix, this prefix // should not be taken in consideration when matching with a pattern String prefix = DefaultTabItem.DIRTY_PREFIX; if (matchName.startsWith(prefix)) { matchName = matchName.substring(prefix.length()); } return matchName != null && matcher.match(matchName); } } private static class BorderFillLayout extends Layout { /** The border widths. */ final int fBorderSize; /** * Creates a fill layout with a border. */ public BorderFillLayout(int borderSize) { if (borderSize < 0) { throw new IllegalArgumentException(); } fBorderSize = borderSize; } /* * @see org.eclipse.swt.widgets.Layout#computeSize(org.eclipse.swt.widgets.Composite, * int, int, boolean) */ protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { Control[] children = composite.getChildren(); Point minSize = new Point(0, 0); if (children != null) { for (int i = 0; i < children.length; i++) { Point size = children[i].computeSize(wHint, hHint, flushCache); minSize.x = Math.max(minSize.x, size.x); minSize.y = Math.max(minSize.y, size.y); } } minSize.x += fBorderSize * 2 + RIGHT_MARGIN; minSize.y += fBorderSize * 2; return minSize; } /* * @see org.eclipse.swt.widgets.Layout#layout(org.eclipse.swt.widgets.Composite, * boolean) */ protected void layout(Composite composite, boolean flushCache) { Control[] children = composite.getChildren(); Point minSize = new Point(composite.getClientArea().width, composite.getClientArea().height); if (children != null) { for (int i = 0; i < children.length; i++) { Control child = children[i]; child.setSize(minSize.x - fBorderSize * 2, minSize.y - fBorderSize * 2); child.setLocation(fBorderSize, fBorderSize); } } } } /** Border thickness in pixels. */ private static final int BORDER = 1; /** Right margin in pixels. */ private static final int RIGHT_MARGIN = 3; /** The control's shell */ private Shell fShell; /** The composite */ protected Composite fComposite; /** The control's text widget */ private Text fFilterText; /** The control's table widget */ private TableViewer fTableViewer; /** The control width constraint */ //private int fMaxWidth= -1; /** The control height constraint */ //private int fMaxHeight= -1; /** The current string matcher */ private StringMatcher fStringMatcher; /** * Creates an information control with the given shell as parent. The given * styles are applied to the shell and the table widget. * * @param parent * the parent shell * @param shellStyle * the additional styles for the shell * @param controlStyle * the additional styles for the control */ public AbstractTableInformationControl(Shell parent, int shellStyle, int controlStyle) { fShell = new Shell(parent, shellStyle); Display display = fShell.getDisplay(); fShell.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); // Composite for filter text and viewer fComposite = new Composite(fShell, SWT.RESIZE); GridLayout layout = new GridLayout(1, false); fComposite.setLayout(layout); // fComposite.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); createFilterText(fComposite); fTableViewer = createTableViewer(fComposite, controlStyle); final Table table = fTableViewer.getTable(); // RAP [bm] KeyListener // table.addKeyListener(new KeyListener() { // public void keyPressed(KeyEvent e) { // switch (e.keyCode) { // case SWT.ESC: // dispose(); // break; // case SWT.DEL: // removeSelectedItems(); // e.character = SWT.NONE; // e.doit = false; // break; // case SWT.ARROW_UP: // if (table.getSelectionIndex() == 0) { // // on the first item, going up should grant focus to // // text field // fFilterText.setFocus(); // } // break; // case SWT.ARROW_DOWN: // if (table.getSelectionIndex() == table.getItemCount() - 1) { // // on the last item, going down should grant focus to // // the text field // fFilterText.setFocus(); // } // break; // } // } // // public void keyReleased(KeyEvent e) { // // do nothing // } // }); table.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { // do nothing; } public void widgetDefaultSelected(SelectionEvent e) { gotoSelectedElement(); } }); /* * Bug in GTK, see SWT bug: 62405 Editor drop down performance slow on * Linux-GTK on mouse move. * Rather then removing the support altogether this feature has been * worked around for GTK only as we expect that newer versions of GTK * will no longer exhibit this quality and we will be able to have the * desired support running on all platforms. See * comment https://bugs.eclipse.org/bugs/show_bug.cgi?id=62405#c22 * TODO: remove this code once bug 62405 is fixed for the mainstream GTK * version */ // RAP [bm]: // final int ignoreEventCount = Platform.getWS().equals(Platform.WS_GTK) ? 4 : 1; // table.addMouseMoveListener(new MouseMoveListener() { // TableItem fLastItem = null; // int lastY = 0; // int itemHeightdiv4 = table.getItemHeight() / 4; // int tableHeight = table.getBounds().height; // Point tableLoc = table.toDisplay(0,0); // int divCount = 0; // public void mouseMove(MouseEvent e) { // if (divCount == ignoreEventCount) { // divCount = 0; // } // if (table.equals(e.getSource()) & ++divCount == ignoreEventCount) { // Object o = table.getItem(new Point(e.x, e.y)); // if (o instanceof TableItem && lastY != e.y) { // lastY = e.y; // if (!o.equals(fLastItem)) { // fLastItem = (TableItem) o; // table.setSelection(new TableItem[] { fLastItem }); // } else if (e.y < itemHeightdiv4) { // // Scroll up // Item item = fTableViewer.scrollUp(e.x + tableLoc.x, e.y + tableLoc.y); // if (item instanceof TableItem) { // fLastItem = (TableItem) item; // table.setSelection(new TableItem[] { fLastItem }); // } // } else if (e.y > tableHeight - itemHeightdiv4) { // // Scroll down // Item item = fTableViewer.scrollDown(e.x + tableLoc.x, e.y + tableLoc.y); // if (item instanceof TableItem) { // fLastItem = (TableItem) item; // table.setSelection(new TableItem[] { fLastItem }); // } // } // } // } // } // }); // RAPEND: [bm] // RAP [rh] MouseListener on Table don't work reliably // table.addMouseListener(new MouseAdapter() { // public void mouseUp(MouseEvent e) { // if (table.getSelectionCount() < 1) { // return; // } // // if (e.button == 1) { // if (table.equals(e.getSource())) { // Object o = table.getItem(new Point(e.x, e.y)); // TableItem selection = table.getSelection()[0]; // if (selection.equals(o)) { // gotoSelectedElement(); // } // } // } // if (e.button == 3) { // TableItem tItem = fTableViewer.getTable().getItem( // new Point(e.x, e.y)); // if (tItem != null) { // Menu menu = new Menu(fTableViewer.getTable()); // MenuItem mItem = new MenuItem(menu, SWT.NONE); // mItem.setText(WorkbenchMessages.get().PartPane_close); // mItem.addSelectionListener(new SelectionAdapter() { // public void widgetSelected( // SelectionEvent selectionEvent) { // removeSelectedItems(); // } // }); // menu.setVisible(true); // } // } // } // }); // RAP [bm] TraverseListener // fShell.addTraverseListener(new TraverseListener() { // public void keyTraversed(TraverseEvent e) { // switch (e.detail) { // case SWT.TRAVERSE_PAGE_NEXT: // e.detail = SWT.TRAVERSE_NONE; // e.doit = true; // { // int n = table.getItemCount(); // if (n == 0) // return; // // int i = table.getSelectionIndex() + 1; // if (i >= n) // i = 0; // table.setSelection(i); // } // break; // // case SWT.TRAVERSE_PAGE_PREVIOUS: // e.detail = SWT.TRAVERSE_NONE; // e.doit = true; // { // int n = table.getItemCount(); // if (n == 0) // return; // // int i = table.getSelectionIndex() - 1; // if (i < 0) // i = n - 1; // table.setSelection(i); // } // break; // } // } // }); int border = ((shellStyle & SWT.NO_TRIM) == 0) ? 0 : BORDER; fShell.setLayout(new BorderFillLayout(border)); setInfoSystemColor(); installFilter(); } /** * Removes the selected items from the list and closes their corresponding tabs * Selects the next item in the list or disposes it if its presentation is disposed */ protected void removeSelectedItems() { int selInd = fTableViewer.getTable().getSelectionIndex(); if (deleteSelectedElements()) { return; } fTableViewer.refresh(); if (selInd >= fTableViewer.getTable().getItemCount()) { selInd = fTableViewer.getTable().getItemCount() - 1; } if (selInd >= 0) { fTableViewer.getTable().setSelection(selInd); } } protected abstract TableViewer createTableViewer(Composite parent, int style); public TableViewer getTableViewer() { return fTableViewer; } protected Text createFilterText(Composite parent) { fFilterText = new Text(parent, SWT.NONE); GridData data = new GridData(); GC gc = new GC(parent); gc.setFont(parent.getFont()); FontMetrics fontMetrics = gc.getFontMetrics(); gc.dispose(); data.heightHint = org.eclipse.jface.dialogs.Dialog .convertHeightInCharsToPixels(fontMetrics, 1); data.horizontalAlignment = GridData.FILL; data.verticalAlignment = GridData.BEGINNING; fFilterText.setLayoutData(data); // RAP [bm]: // fFilterText.addKeyListener(new KeyListener() { // public void keyPressed(KeyEvent e) { // if (e.keyCode == 0x0D) { // gotoSelectedElement(); // } // if (e.keyCode == SWT.ARROW_DOWN) { // fTableViewer.getTable().setFocus(); // fTableViewer.getTable().setSelection(0); // } // if (e.keyCode == SWT.ARROW_UP) { // fTableViewer.getTable().setFocus(); // fTableViewer.getTable().setSelection( // fTableViewer.getTable().getItemCount() - 1); // } // if (e.character == 0x1B) { // dispose(); // } // } // // public void keyReleased(KeyEvent e) { // // do nothing // } // }); // RAPEND: [bm] // Horizontal separator line // RAP [bm]: SWT.LINE_DOT // Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL // | SWT.LINE_DOT); Label separator = new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL); // RAPEND: [bm] separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); return fFilterText; } private void setInfoSystemColor() { Display display = fShell.getDisplay(); setForegroundColor(display.getSystemColor(SWT.COLOR_INFO_FOREGROUND)); setBackgroundColor(display.getSystemColor(SWT.COLOR_INFO_BACKGROUND)); } private void installFilter() { fFilterText.setText(""); //$NON-NLS-1$ fFilterText.addModifyListener(new ModifyListener() { public void modifyText(ModifyEvent e) { String text = ((Text) e.widget).getText(); int length = text.length(); if (length > 0 && text.charAt(length - 1) != '*') { text = text + '*'; } setMatcherString(text); } }); } /** * The string matcher has been modified. The default implementation * refreshes the view and selects the first macthed element */ protected void stringMatcherUpdated() { // refresh viewer to refilter fTableViewer.getControl().setRedraw(false); fTableViewer.refresh(); selectFirstMatch(); fTableViewer.getControl().setRedraw(true); } /** * Sets the patterns to filter out for the receiver. * <p> * The following characters have special meaning: ? => any character * => * any string * </p> */ protected void setMatcherString(String pattern) { if (pattern.length() == 0) { fStringMatcher = null; } else { boolean ignoreCase = pattern.toLowerCase().equals(pattern); fStringMatcher = new StringMatcher(pattern, ignoreCase, false); } stringMatcherUpdated(); } protected StringMatcher getMatcher() { return fStringMatcher; } /** * Implementers can modify */ protected Object getSelectedElement() { return ((IStructuredSelection) fTableViewer.getSelection()) .getFirstElement(); } /** * Implementers can modify */ protected IStructuredSelection getSelectedElements() { return (IStructuredSelection) fTableViewer.getSelection(); } protected abstract void gotoSelectedElement(); /** * Delete all selected elements. * * @return <code>true</code> if there are no elements left after deletion. */ protected abstract boolean deleteSelectedElements(); /** * Selects the first element in the table which matches the current filter * pattern. */ protected void selectFirstMatch() { Table table = fTableViewer.getTable(); Object element = findElement(table.getItems()); if (element != null) { fTableViewer.setSelection(new StructuredSelection(element), true); } else { fTableViewer.setSelection(StructuredSelection.EMPTY); } } private Object findElement(TableItem[] items) { ILabelProvider labelProvider = (ILabelProvider) fTableViewer .getLabelProvider(); for (int i = 0; i < items.length; i++) { Object element = items[i].getData(); if (fStringMatcher == null) { return element; } if (element != null) { String label = labelProvider.getText(element); if(label == null) { return null; } // remove the dirty prefix from the editor's label String prefix = DefaultTabItem.DIRTY_PREFIX; if (label.startsWith(prefix)) { label = label.substring(prefix.length()); } if (fStringMatcher.match(label)) { return element; } } } return null; } public abstract void setInput(Object information); protected void inputChanged(Object newInput, Object newSelection) { fFilterText.setText(""); //$NON-NLS-1$ fTableViewer.setInput(newInput); selectFirstMatch(); // Resize the table's height accordingly to the new input Table viewerTable = fTableViewer.getTable(); Point tableSize = viewerTable.computeSize(SWT.DEFAULT, SWT.DEFAULT); int tableMaxHeight = fComposite.getDisplay().getBounds().height / 2; // removes padding if necessary int tableHeight = (tableSize.y <= tableMaxHeight) ? tableSize.y - viewerTable.getItemHeight() - viewerTable.getItemHeight() / 2 : tableMaxHeight; ((GridData) viewerTable.getLayoutData()).heightHint = tableHeight; Point fCompSize = fComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT); fComposite.setSize(fCompSize); fComposite.getShell().setSize(fCompSize); } public void setVisible(boolean visible) { fShell.setVisible(visible); } public void dispose() { if (fShell != null) { if (!fShell.isDisposed()) { fShell.dispose(); } fShell = null; fTableViewer = null; fComposite = null; fFilterText = null; } } public boolean hasContents() { return fTableViewer != null && fTableViewer.getInput() != null; } public void setSizeConstraints(int maxWidth, int maxHeight) { //fMaxWidth= maxWidth; //fMaxHeight= maxHeight; } public Point computeSizeHint() { return fShell.computeSize(SWT.DEFAULT, SWT.DEFAULT); } public void setLocation(Point location) { Rectangle trim = fShell.computeTrim(0, 0, 0, 0); Point textLocation = fComposite.getLocation(); location.x += trim.x - textLocation.x; location.y += trim.y - textLocation.y; fShell.setLocation(location); } public void setSize(int width, int height) { fShell.setSize(width, height); } public void addDisposeListener(DisposeListener listener) { fShell.addDisposeListener(listener); } public void removeDisposeListener(DisposeListener listener) { fShell.removeDisposeListener(listener); } public void setForegroundColor(Color foreground) { fTableViewer.getTable().setForeground(foreground); fFilterText.setForeground(foreground); fComposite.setForeground(foreground); } public void setBackgroundColor(Color background) { fTableViewer.getTable().setBackground(background); fFilterText.setBackground(background); fComposite.setBackground(background); } public boolean isFocusControl() { return fTableViewer.getControl().isFocusControl() || fFilterText.isFocusControl(); } public void setFocus() { fShell.forceFocus(); fFilterText.setFocus(); } public void addFocusListener(FocusListener listener) { fShell.addFocusListener(listener); } public void removeFocusListener(FocusListener listener) { fShell.removeFocusListener(listener); } }